package com.theten52.demo.demo1;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 测试String字面量最大是多少
 *
 * @author wangjin
 * @date 2020/6/10
 */
public class StringDemo {

    public static void main(String[] args) throws IOException {
        String fileName = "./Literal.java";
        StringBuilder prefix = new StringBuilder();
        prefix.append("public class Literal {String s = \"");
        int low = 0;
        int high = 1000000;     //指定一个较大的值
        int mid = (high + low) / 2;
        StringBuilder literal = new StringBuilder(high);
        int result;             //编译结果
        String ch = "A";
        //取得系统编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //自定义错误输出流，取代System类的err。
        OutputStream err = new OutputStream() {
            @Override
            public void write(byte b[], int off, int len) {
                /*
                 *什么也不做，即使在产生编译错误时，也不在控制台上
                 *输出任何错误信息，以免干扰正常的输出信息。
                 */
            }

            @Override
            public void write(int b) {
                //重写父类的抽象方法，这里什么也不做。
            }
        };
        int max = 0;
        for (int i = 0; i < mid; i++) {
            /*
             *将String的字面值追加到指定的长度。注意这个长度是代码点的长
             *度，而不是代码单元（char类型的字符）的长度。换句话说，如果
             *ch是增补字符，那么每次循环将追加两个char类型的字符。
             */
            literal.append(ch);
        }
        while (low <= high) {
            StringBuilder fileContent = new StringBuilder(literal.length() + prefix.length() * 2);
            fileContent.append(prefix);
            fileContent.append(literal);
            fileContent.append("\";}");
            FileWriter w = new FileWriter(fileName);
            BufferedWriter bw = new BufferedWriter(w);
            bw.write(fileContent.toString());
            bw.close();
            w.close();
            //编译生成的d:/Literal.java文件。
            result = compiler.run(null, null, err, fileName);
            //代码点的数量
            int codePointCount = literal.codePointCount(0, literal.length());
            //如果结果是0，表明编译结果没有错误，可以尝试更长的字面常量。
            if (result == 0) {
                low = mid + 1;
                mid = (high + low) / 2;
                max = codePointCount;
                //将字面常量的长度增加到mid。
                for (int i = codePointCount; i < mid; i++) {
                    literal.append(ch);
                }
                System.out.println("长度" + max + "编译成功，增加其长度至" + mid);
            } else {
                //如果结果是非0，表明编译结果有误，这说明字面常量过长。
                high = mid - 1;
                mid = (high + low) / 2;
                System.out.println("长度" + codePointCount + "编译失败，减少其长度至" + mid);
                /*
                 *将字面常量的长度减少到mid。如果String对象ch表示的是基本
                 *多语言面字符，则一个字符使用一个char类型表示，删除mid之
                 *后的所有字符，如果ch表示的是增补字符，则一个字符使用两个
                 *char类型表示，删除mid *2 后面的所有字符。
                 */
                int start = ch.length() == 1 ? mid : mid * 2;
                literal.delete(start, literal.length());
            }
        }
        err.close();
        System.out.println("最大的字面常量长度是：" + max);
    }

}
